package com.springboot.frame.mapper.provider;

import com.springboot.frame.annotation.AutoGenerator;
import com.springboot.frame.annotation.ColumnType;
import com.springboot.frame.annotation.LogicDelete;
import com.springboot.frame.key.AutoKeyGenerator;
import com.springboot.frame.key.SnowFlakeKeyGenerator;
import com.springboot.frame.key.UUID32KeyGenerator;
import com.springboot.frame.key.UUIDKeyGenerator;
import com.springboot.frame.schema.Column;
import com.springboot.frame.schema.LogicColumn;
import com.springboot.frame.schema.PrimaryKey;
import com.springboot.frame.schema.Table;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.annotation.ProviderContext;

import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import java.beans.Transient;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.ParameterizedType;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;


/**
 * 可参考文档
 * https://www.jianshu.com/p/5fcea00f439d
 */
abstract class AbstractMapperProvider {

    /**
     * mapping缓存
     */
    private static final Map<Class<?>, Table> TABLE_MAP = new HashMap<>();

    /**
     * 条件之间的分隔符
     */
    protected String separator = " ";

    /**
     * 大写转下划线
     *
     * @param str 原始名称
     * @return 转换后名称
     */
    private static String camelToUnderline(String str) {
        if (StringUtils.isNotBlank(str)) {
            StringBuilder result = new StringBuilder(str.length());
            char first = str.charAt(0);
            if (Character.isUpperCase(first)) {
                result.append(Character.toLowerCase(first));
            } else {
                result.append(first);
            }
            for (int i = 1; i < str.length(); i++) {
                char charAt = str.charAt(i);
                if (Character.isUpperCase(charAt)) {
                    result.append("_").append(Character.toLowerCase(charAt));
                } else {
                    result.append(charAt);
                }
            }
            return result.toString();
        }
        return str;
    }

    /**
     * 获取表
     *
     * @param providerContext
     * @return
     */
    protected Table getTable(ProviderContext providerContext) {
        Class<?> mapperType = providerContext.getMapperType();
        Table table = TABLE_MAP.get(mapperType);
        if (table == null) {
            return cacheTable(mapperType);
        } else {
            return table;
        }

    }

    /**
     * 获取table并缓存
     * @param mapperType
     * @return
     */
    private Table cacheTable(Class<?> mapperType) {
        ParameterizedType parameterizedType = (ParameterizedType) mapperType.getGenericInterfaces()[0];
        Class<?> entityClazz = (Class<?>) parameterizedType.getActualTypeArguments()[0];
        String tableName = camelToUnderline(entityClazz.getSimpleName());
        Set<Column> columns = new LinkedHashSet<>();
        String selectBody = "*";
        LogicColumn logicColumn = null;
        PrimaryKey primaryKey = null;
        // 上下文需要的参数
        StringBuilder tempSelectBody = new StringBuilder();
        // 处理实体
        if (entityClazz.isAnnotationPresent(javax.persistence.Table.class)) {
            String tempTableName = entityClazz.getAnnotation(javax.persistence.Table.class).name();
            if (StringUtils.isNotEmpty(tempTableName)) {
                tableName = tempTableName;
            }
        }
        // 处理实体字段
        for (Class<?> clazz = entityClazz; clazz != Object.class; clazz = clazz.getSuperclass()) {
            for (Field field : clazz.getDeclaredFields()) {
                // 不需要与数据库映射的字段
                if (field.isAnnotationPresent(Transient.class)) {
                    continue;// 跳过
                }
                if (!Modifier.isStatic(field.getModifiers()) && !Modifier.isFinal(field.getModifiers())) {
                    if (!field.isAccessible()) {
                        field.setAccessible(true);
                    }
                    String propertyName = field.getName();
                    String columnName = camelToUnderline(propertyName);
                    Column mappingField = new Column();
                    if (field.isAnnotationPresent(javax.persistence.Column.class)) {
                        javax.persistence.Column column = field.getAnnotation(javax.persistence.Column.class);
                        String tempColumnName = column.name();
                        columnName = StringUtils.isNotEmpty(tempColumnName) ? tempColumnName : columnName;
                        mappingField.setNullAble(column.nullable());
                        mappingField.setInsertAble(column.insertable());
                        mappingField.setUpdateAble(column.updatable());
                    }
                    if (field.isAnnotationPresent(ColumnType.class)) {
                        mappingField.setJdbcType(
                                ",jdbcType=" + field.getAnnotation(ColumnType.class).jdbcType().name());
                    }
                    mappingField.setField(field);
                    mappingField.setPropertyName(propertyName);
                    mappingField.setColumnName(columnName);
                    // 主键特殊处理
                    primaryKey = addPrimaryKey(primaryKey, columns, field, propertyName, columnName,
                            mappingField);
                    // 逻辑删除处理
                    if (field.isAnnotationPresent(LogicDelete.class)) {
                        LogicDelete logicDelete = field.getAnnotation(LogicDelete.class);
                        logicColumn = new LogicColumn(columnName, logicDelete.value(), logicDelete.delValue());
                    }

                    tempSelectBody.append(columnName)
                            .append(propertyName.equals(columnName) ? "," : " as " + propertyName + ",");


                }
            }
        }
        if (tempSelectBody.length() > 0) {
            selectBody = tempSelectBody.deleteCharAt(tempSelectBody.length() - 1).toString();
        }
        // 构建并缓存
        Table table = Table.builder()
                .tableName(tableName)
                .primaryKey(primaryKey)
                .selectBody(selectBody)
                .columns(columns)
                .logicColumn(logicColumn)
                .build();
        TABLE_MAP.put(mapperType, table);
        return table;
    }

    private PrimaryKey addPrimaryKey(PrimaryKey primaryKey, Set<Column> columns, Field field,
                                     String propertyName,
                                     String columnName, Column column) {
        //if (field.isAnnotationPresent(Id.class) || ID.equals(column.getPropertyName())) {
        // 不指定主键id列，自动获取（id自增）
        if (field.isAnnotationPresent(Id.class)) {
            GenerationType generationType = GenerationType.IDENTITY;
            SequenceGenerator sequenceGenerator = null;
            AutoKeyGenerator<?> autoKeyGenerator = null;
            // 特殊主键处理
            if (field.isAnnotationPresent(GeneratedValue.class)) {
                GeneratedValue generatedValue = field.getAnnotation(GeneratedValue.class);
                generationType = generatedValue.strategy();
                switch (generationType) {
                    case SEQUENCE:
                        sequenceGenerator = field.getAnnotation(SequenceGenerator.class);
                        if (sequenceGenerator == null) {
                            throw new NullPointerException("定义主键策略为SEQUENCE但是未设置SequenceGenerator");
                        }
                        break;
                    case AUTO:
                        if (!field.isAnnotationPresent(AutoGenerator.class)) {
                            autoKeyGenerator = SnowFlakeKeyGenerator.INSTANCE;
                        } else {
                            switch (field.getAnnotation(AutoGenerator.class).idType()) {
                                case UUID:
                                    autoKeyGenerator = UUIDKeyGenerator.INSTANCE;
                                    break;
                                case UUID32:
                                    autoKeyGenerator = UUID32KeyGenerator.INSTANCE;
                                    break;
                                default:
                                    autoKeyGenerator = SnowFlakeKeyGenerator.INSTANCE;
                                    break;
                            }
                        }
                    default:
                        break;
                }
            }
            primaryKey = new PrimaryKey(field, propertyName, columnName, generationType,
                    sequenceGenerator, autoKeyGenerator);
        } else {
            columns.add(column);
        }
        return primaryKey;
    }

}
